/**
 * @file cm_video_test.c
 * @author jmdvirus
 * @date 2021-06-29 10:30:51
 */

#include <assert.h>
#include <stdio.h>
#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include "rt_print.h"
#include "cm_video_ctrl.h"
#include "cm_utils.h"

void test_video()
{
    const CMVideoImpl *impl = cm_video_impl_find("notexist");
    assert(impl == NULL);
    const CMVideoImpl *impl_r = cm_video_impl_find("v4l2-normal");
    assert(impl_r != NULL);
}

// 这种测试用于多次开关测试
// 和测试时看 cpu 和内存占用率是否有异常
void test_video_record(int index, const char *dev, const char *savepath, int width, int height, int fps, int type,
                       int wait, int platform, int f_count, int dst_format)
{
    CMVideoContext ctx;
    ctx.priv_data = NULL;
    rtdebug("Start [%s]", dev);

    const CMVideoImpl *impl = NULL;

    impl = cm_video_impl_find("v4l2-normal");

    assert(impl != NULL);

    int ret = impl->open(&ctx, dev);
    if (ret < 0)
    {
        rterror("open fail [%s] [%d]", dev, ret);
        return;
    }
    int v = CM_VIDEO_FORMAT_YUYV;
    if (type == 2)
    {
        v = CM_VIDEO_FORMAT_JPEG;
    }
    else if (type == 3)
    {
        v = CM_VIDEO_FORMAT_H264;
    }
    else if (type == 4)
    {
        v = CM_VIDEO_FORMAT_H265;
    }
    impl->ctrl(&ctx, CM_VIDEO_CMD_SET_CHANNEL, &index);
    impl->ctrl(&ctx, CM_VIDEO_CMD_SET_WIDTH, &width);
    impl->ctrl(&ctx, CM_VIDEO_CMD_SET_HEIGHT, &height);
    impl->ctrl(&ctx, CM_VIDEO_CMD_SET_FPS, &fps);
    ret = impl->ctrl(&ctx, CM_VIDEO_CMD_SET_PIX_FORMAT, &v);
    if (ret < 0)
    {
        rterror("set format fail [%d] [%s]", ret, dev);
        return;
    }
    rtdebug("excute start [%s]", dev);
    ret = impl->start(&ctx);
    if (ret < 0)
    {
        rterror("start fail [%d] [%s]", ret, dev);
        impl->close(&ctx);
        return;
    }

    rtdebug("start to read data [%s]", dev);
    FILE *f = NULL;
    if (savepath)
    {
        f = fopen(savepath, "w");

        if (!f)
        {
            rterror("write file [%s] fail [%s]", savepath, dev);
        }
        else
        {
            rtinfo("write file [%s] create success!!!![%s] ", savepath, dev);
        }
    }
    CMVideoBuf buf;
    time_t     last         = time(NULL);
    size_t     all_length   = 0;
    int        ri           = 0;
    int        exit_count   = 0;
    int        fcount       = 0;
    time_t     fcount_start = time(NULL);
    unsigned long long r_end;
    while (1)
    {
        // rtinfo("curr time:%ld last:%ld, wait:%ld", time(NULL), last, wait);
        if (time(NULL) - last > wait)
        {
            rtdebug("wait timeout, exit [%s]", dev);
            break;
        }
        unsigned long long r_last = cm_gettime_milli();
        unsigned long long r_whole_usage = r_last - r_end;
        buf.length = 0;
        //         rtinfo("start read data");
        unsigned long long r_start = cm_gettime_milli();
        ret = impl->read(&ctx, &buf);
        //         rtinfo("read ret:%ld", ret);
        r_end = cm_gettime_milli();
        if (ret <= 0)
        {
             rterror("read fail [%d]", ret);
            if (ret < 0)
            {
                rterror("read fail [%d] [%s]", ret, dev);
            }
            usleep(10 * 1000);
            continue;
        }
        else
        {
            rtdebug("no data");
        }
        rtinfo("read size:%ld [%s] [%p] usage [%llu] [%llu]", buf.length, dev, &ctx, r_end - r_start, r_whole_usage);

        if (v == CM_VIDEO_FORMAT_JPEG && dst_format != CM_VIDEO_FORMAT_H264 && dst_format != CM_VIDEO_FORMAT_H265)
        {
            if (savepath)
            {
                char xx[1024] = {0};
                sprintf(xx, "%s-%d.jpg", savepath, ri++);
                FILE *fm = fopen(xx, "w");
                if (fm)
                {
                    ret = fwrite(buf.start, 1, buf.length, fm);
                    rtdebug("write file [%s] len [%d] [%d] [%s]", xx, ret, buf.length, dev);
                    fclose(fm);
                }
            }
            fcount++;
            if ((time(NULL) - fcount_start) >= 10)
            {
                rtinfo("frame count %d fps/(%d s) [%s]\n", fcount, 10, dev);
                fcount_start = time(NULL);
                fcount       = 0;
            }
            if (exit_count++ >= f_count)
            {
                rtdebug("enter max frame count [%d][%s]", f_count, dev);
                break;
            }
        }

        if ((v != CM_VIDEO_FORMAT_JPEG || dst_format == CM_VIDEO_FORMAT_H264 || dst_format == CM_VIDEO_FORMAT_H265))
        {
            if (f)
            {
                ret = fwrite(buf.start, 1, buf.length, f);
            }
            else
            {
                ret = buf.length;
            }
            static int count = 0;
            all_length += ret;
            if (count++ == 20)
            {
                rtdebug("write length of [%d] all length [%lu][%s]", ret, all_length, dev);
                count = 0;
            }
            fcount++;
            if ((time(NULL) - fcount_start) >= 10)
            {
                rtinfo("frame count %d fps/(%d s) [%s]\n", fcount, 10, dev);
                fcount_start = time(NULL);
                fcount       = 0;
            }

            if (exit_count++ >= f_count)
            {
                rtdebug("enter max frame count [%d] [%s]", f_count, dev);
                break;
            }
        }
    }
    rtdebug("all done length [%lu] [%s]", all_length, dev);
    impl->stop(&ctx);
    impl->close(&ctx);
    if (f)
    {
        fclose(f);
    }
    rtinfo("done [%s]", dev);
}

int main(int argc, char **args)
{
    char *dev        = NULL;
    char *save       = NULL;
    int   c          = -1;
    int   w          = 1280;
    int   h          = 720;
    int   fps        = 25;
    int   t          = 0;
    int   wait       = 2; // 15;
    int   a          = 0;
    int   platform   = 0;
    int   tryTime    = 1;
    int   f_count    = 60;
    int   index      = 0;
    int   dst_format = CM_VIDEO_FORMAT_NONE;
    rtdebug("program startup [%s:%s]", __DATE__, __TIME__);
    while ((c = getopt(argc, args, "d:s:t:w:h:f:m:p:a:y:c:i:z:")) != -1)
    {
        switch (c)
        {
        case 'd':
            dev = optarg;
            break;
        case 's':
            save = optarg;
            break;
        case 't':
            t = strtol(optarg, NULL, 10);
            break;
        case 'a':
            a = strtol(optarg, NULL, 10);
            break;
        case 'w':
            w = strtol(optarg, NULL, 10);
            break;
        case 'h':
            h = strtol(optarg, NULL, 10);
            break;
        case 'f':
            fps = strtol(optarg, NULL, 10);
            break;
        case 'm':
            wait = strtol(optarg, NULL, 10);
            break;
        case 'p':
            platform = strtol(optarg, NULL, 10);
            break;
        case 'y':
            tryTime = strtol(optarg, NULL, 10);
            break;
        case 'c':
            f_count = strtol(optarg, NULL, 10);
            break;
        case 'i':
            index = strtol(optarg, NULL, 10);
            break;
        case 'z':
            dst_format = strtol(optarg, NULL, 10);
            break;
        default:
            break;
        }
    }

    printf("[%s][%s] Operation Type [%d] a = [%d] (a-0-autotest, a-1 - two usb, 11 - rkusb 12 - two rkusb), "
           "-t ( t-1 yuv t-2 mjpeg  t-3 h264 t-4 h265) -z (1 h264 2 h265)\n",
           __DATE__, __TIME__, t, a);
    if (a == 0)
    {
        test_video();
    }
    else if (a == 1)
    {
        if (dev == NULL)
        {
            printf("Usage: %s -d(device) -s (save name) -t (type 1-yuv 2-mjpeg 3-h264 4-h265) -w (width) -h(height) "
                   "-f(fps) -m "
                   "(waittime(15)) -p (platform 1 v4l26)\n"
                   "-y (tryTimes) -z (1 h264 2 h265)",
                   args[0]);
            return 0;
        }
        rtdebug("Operation index [%d] dev [%s] save [%s] wh [%d, %d] fps [%d] wait [%d] platform [%d] tryTime [%d] fps "
               "count [%d]"
               " dst_format [%d]\n",
               index, dev, save, w, h, fps, wait, platform, tryTime, f_count, dst_format);
        // 测试多次打设备采集图像
        int time = tryTime;
        for (int i = 0; i < time; i++)
        {
            printf("\n\n******************************%d*********************\n", i);
            char thepath[1024] = {0};
            if (save)
            {
                snprintf(thepath, sizeof(thepath), "%s.%d", save, i);
                test_video_record(index, dev, thepath, w, h, fps, t, wait, platform, f_count, dst_format);
            }
            else
            {
                test_video_record(index, dev, NULL, w, h, fps, t, wait, platform, f_count, dst_format);
            }
            if (time > 1)
            {
                sleep(5);
            }
        }
    }
    return 0;
}
